from flask import Flask ,render_template,request
from flask_sockets import Sockets
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from werkzeug.routing import Rule
import json
from flask_cors import CORS
import random
from threading import Timer
import time

''' 
web socket相关
'''

app = Flask("GoMoKu-Server")
sockets = Sockets(app)

CORS(app, supports_credentials=True, resources=r'/*')

'''
判断五子棋获胜与否的算法
'''
def is_win(boardcontent, location, color):
    # boardcontent:棋盘内容, location:落子位置, color:落子颜色
    row = location[0]
    col = location[1]
    size = len(boardcontent) - 1
    same = 1
    # 1.一行是否连五子
    while col <= size and boardcontent[row][col] == color : # 向右扫描
        if col != location[1] :
            same = same + 1
        col = col + 1
        if col > size : break
    col = location[1]
    while col >= 0 and boardcontent[row][col] == color : # 向左扫描
        if col != location[1] :
            same = same + 1
        col = col - 1
        if col < 0 : break
    if same == 5 : return True
    
    # 2.一列是否连五子
    row = location[0]
    col = location[1]
    same = 1
    while row <= size and boardcontent[row][col] == color : # 向上扫描
        if row != location[0] :
            same = same + 1
        row = row + 1
        if row > size : break
    row = location[0]
    while row >= 0 and boardcontent[row][col] == color : # 向下扫描
        if row != location[0] :
            same = same + 1
        row = row - 1
        if row < 0 : break
    if same == 5 : return True

    # 3.对角线"/"是否连五子
    row = location[0]
    col = location[1]
    same = 1
    while row >= 0 and col <= size and boardcontent[row][col] == color : # 向右上扫描
        if [row, col] != location:
            same = same + 1
        col = col + 1
        row = row - 1
        if col > size or row < 0: break
    row = location[0]
    col = location[1]
    while row <= size  and col >= 0 and boardcontent[row][col] == color : # 向左下扫描
        if [row, col] != location:
            same = same + 1
        col = col - 1
        row = row + 1
        if col < 0 or row > size: break
    if same == 5 : return True

    # 4.对角线"\"是否连五子
    row = location[0]
    col = location[1]
    same = 1
    while row >= 0 and col >= 0 and boardcontent[row][col] == color : # 向左上扫描
        if [row, col] != location:
            same = same + 1
        col = col - 1
        row = row - 1
        if col < 0 or row < 0: break
    row = location[0]
    col = location[1]
    while row <= size  and col <= size and boardcontent[row][col] == color : # 向右下扫描
        if [row, col] != location:
            same = same + 1
        col = col + 1
        row = row + 1
        if col > size or row > size: break
    if same == 5 : return True
    return False




'''
一些全局变量
'''

playernum = 0 # 当前用户数
roomnum = 0 # 当前房间数

players = {} # 用户字典
rooms = {} # 房间字典
pairplayers = [] # 等待匹配用户id列表


# msg type
register = 0 # 注册
pair = 1 # 配对
move = 2 # 落子
end = 3 # 结束
close = 4 # 非正常结束

# 状态
yes = 1
no = 0

# 棋子颜色
black = 2
white = 1

# 用户对象
class player:
    id = 0
    name = ""
    oppentid = None # 对手
    roomid = None # 所属房间
    socket = None # web socket
    color = None # 棋子颜色
    def __init__(self, id, name, socket):
        self.id = id
        self.name = name
        self.socket = socket

# 房间对象
class room:
    turncolor = black
    gameover = False
    rounds = 0
    def __init__(self, roomid, playerid1, socket1, playerid2, socket2):
        self.id = roomid
        self.playerid1 = playerid1
        self.socket1 = socket1
        self.playerid2 = playerid2
        self.socket2 = socket2
        self.boardcontent = [[0 for j in range(10)] for i in range(10)]
    
    # 修改执棋方
    def changeturn(self):
        if(self.turncolor == black): 
            self.turncolor = white
        else:
            self.turncolor = black

    # 是否允许下棋
    def movechess(self, playerid, location):
        if(self.turncolor == players[playerid].color and playerid in [self.playerid1, self.playerid2]): # 轮到该方落子
            if self.boardcontent[location[0]][location[1]] != 0 : # 该格已经落子了
                return False
            elif self.gameover:
                return False
            else:
                self.boardcontent[location[0]][location[1]] = players[playerid].color # 修改棋盘
                if players[playerid].color == black:
                    self.rounds = self.rounds + 1
                self.changeturn()
                return True
        else:
            return False
    
    # 判断是否获胜，跟在move_chess后使用
    def checkwin(self, playerid, location):
        return is_win(self.boardcontent, location, players[playerid].color)

# 匹配和维护房间算法
def pairgame():
    global playernum, roomnum, players, rooms, pairplayers, register, pair, move, end, yes, no, black, white
    
    # 检查pairplayers
    removeid = []
    for playerid in pairplayers:
        if players[playerid].socket.closed :
            removeid.append(playerid)
    for i in removeid :
        pairplayers.remove(i)
    
    # 匹配
    while len(pairplayers) > 1:
        playerid1 = pairplayers[0]
        playerid2 = pairplayers[1]
        del pairplayers[0]
        del pairplayers[0]
        players[playerid1].opponentid = playerid2
        players[playerid2].opponentid = playerid1

        rooms[roomnum] = room(roomnum, playerid1, players[playerid1].socket, playerid2, players[playerid2].socket)
        players[playerid1].roomid = roomnum
        players[playerid2].roomid = roomnum
        roomnum += 1

        players[playerid1].color = black
        players[playerid2].color = white

        players[playerid1].socket.send(json.dumps({'type':pair,'status':yes, 'color':black, 'opponent':players[playerid2].name}))
        players[playerid2].socket.send(json.dumps({'type':pair,'status':yes, 'color':white, 'opponent':players[playerid1].name}))


        print("房间创建成功", playerid1, playerid2)
        print(roomnum)

    
    # 维护
    if len(rooms.keys()) > 0:
        delroomid = []
        for roomid in rooms.keys():
            room_check = rooms[roomid] # 待检查的房间
            playerid1 = room_check.playerid1
            playerid2 = room_check.playerid2
            if players[playerid1].socket.closed and not players[playerid2].socket.closed: # 玩家1离开房间，通知玩家2
                players[playerid2].socket.send(json.dumps({'type':close,'status':yes}))
                if roomid not in delroomid:
                    delroomid.append(roomid)
            elif players[playerid2].socket.closed and not players[playerid1].socket.closed: # 玩家2离开房间，通知玩家1
                players[playerid1].socket.send(json.dumps({'type':close,'status':yes}))
                if roomid not in delroomid:
                    delroomid.append(roomid)
        for roomid in delroomid:
            del rooms[roomid]
            
    loop_pair()
    
def loop_pair():
    t = Timer(2, pairgame) # 两秒执行一次
    t.start()



# web socket 通信
@sockets.route('/socket')
def server(socket):
    global playernum, roomnum, players, rooms, pairplayers, register, pair, move, end, yes, no, black, white
    # 循环监听, 接收数据, json格式
    while not socket.closed:
        msg_origin = socket.receive()
        msg = json.loads(msg_origin) # json解析
        msg_type = msg["type"]

        # 注册
        if(msg_type == register):
            players[playernum] = player(playernum, msg["playername"], socket)
            socket.send(json.dumps({'type':register,'status':yes, 'playerid':playernum}))
            print("添加用户", playernum, msg["playername"])
            playernum += 1
        
        # 匹配
        elif(msg_type == pair):
            pairplayers.append(msg["playerid"])
            print("想要配对", msg["playerid"])
            loop_pair()

        # 下棋
        elif(msg_type == move):
            room_get = rooms[players[msg["playerid"]].roomid]
            location = [msg["row"], msg["col"]]
            if room_get.movechess(msg["playerid"], location) :
                players[room_get.playerid1].socket.send(json.dumps({'type':move,'status':yes, 'color':players[msg["playerid"]].color, 'row':location[0], 'col':location[1], 'turn':room_get.turncolor, 'rounds':room_get.rounds}))
                players[room_get.playerid2].socket.send(json.dumps({'type':move,'status':yes, 'color':players[msg["playerid"]].color, 'row':location[0], 'col':location[1], 'turn':room_get.turncolor, 'rounds':room_get.rounds}))
            if room_get.checkwin(msg["playerid"], location) :
                del rooms[players[msg["playerid"]].roomid]
                time.sleep(1)
                players[room_get.playerid1].socket.send(json.dumps({'type':end,'status':yes, 'playerid':msg["playerid"]}))
                time.sleep(1)
                players[room_get.playerid2].socket.send(json.dumps({'type':end,'status':yes, 'playerid':msg["playerid"]}))
                


            
                
            
        

# 游戏页面
@app.route('/play')
def GOMOKU_game():
    return render_template("client.html")

if __name__ == '__main__':
    http_serv = WSGIServer(("0.0.0.0",8080),app,handler_class=WebSocketHandler) # 在APP外封装websocket
    sockets.url_map.add(Rule('/socket', endpoint=server, websocket=True)) # 放行websocket
    http_serv.serve_forever() # 启动服务